package com.example.classes;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import android.widget.Toast;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;

/**
 * This class represents the database structure used to store the users objects.
 * @author Ankit Pasi, Alexander Huynh
 * @version 1.0
 */
public class DataBase
{
    private final MyDBHelper help;
    public static final String DATABASE_NAME = "USERLIST";
    public static final String TABLE_USER = "user";
    private static final String COLUMN_USER = "user";
    private static final String COLUMN_PASS = "password";
    private static final String COLUMN_CONTACT = "contact";
    private static final String COLUMN_LOCKS = "locked";
    private static final String COLUMN_ITEMS = "items";
    private static final String COLUMN_ADMIN = "admin";
    public static final String DATABASE_CREATE = "create table " + TABLE_USER + "(" + COLUMN_USER + " text not null primary key, " + COLUMN_PASS + " text not null, " +
    COLUMN_CONTACT + " text not null, " + COLUMN_ITEMS +" text not null, " + COLUMN_LOCKS+" text, " + COLUMN_ADMIN + " text" + " );";

    private SQLiteDatabase data;
    private final Context c;
    private Gson gson;

    /**
     * Constructor.
     * @param context the context to use
     */
    public DataBase(Context context)
    {
        help = new MyDBHelper(context);
        c = context;
        gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
    }

    /**
     * Getter for database data.
     * @return the database object
     */
    public SQLiteDatabase getData()
    {
        return data;
    }

    /**
     * Acquires DataBase helper.
     */
    public void open()
    {
        try
        {
            data = help.getWritableDatabase();
        } catch(Exception e)
        {
            Toast.makeText(c, "Exception while opening database: " + e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Adds a user to the database.
     * @param s the user
     */
    public void createUserEntry(Member s)
    {
        Log.v("WMS-DEBUG", "DATABASE: createUserEntry: " + s.toString());
        try
        {
            this.open();
            String itemList = gson.toJson(s.getItems());
            ContentValues val = new ContentValues();
            val.put(COLUMN_USER, s.getName());
            val.put(COLUMN_PASS, s.getPassword());
            val.put(COLUMN_ITEMS, itemList);
            val.put(COLUMN_LOCKS, 0);
            val.put(COLUMN_CONTACT, s.getContact());
            val.put(COLUMN_ADMIN, (s instanceof Admin));
            data.insert(TABLE_USER, null, val);
            data.close();
        } catch(Exception e)
        {
            Toast.makeText(c, "ERROR: " + e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Function to delete entry from database.
     * @param uname user name of Member to delete
     */
    public void deleteUserEntry(String uname)
    {
        data.delete(TABLE_USER, COLUMN_USER + " = '" + uname + "'", null);
    }

    /**
     * Gets Member data for the given user name.
     * @param uname user name of Member to get data for
     * @return Member object
     */
    public Member getEntry(String uname) throws DBException
    {
        Cursor curse;
        this.open();
        String query= "SELECT * FROM " + TABLE_USER + " WHERE "+COLUMN_USER + " = '"+uname+"'";
        this.open();
        if(data != null)
        {
            curse = data.rawQuery(query,null);
            if (curse.moveToFirst())
            {
                ArrayList<Item> items = new ArrayList<Item>();
                gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
                String userid = curse.getString(0);
                String userpass = curse.getString(1);
                if (userid.equals("") || userpass.equals(""))
                    throw new DBException();
                String contact = curse.getString(2);
                String itemList = curse.getString(3);
                int locks = Integer.parseInt(curse.getString(4));
                boolean isAdmin = (curse.getString(5).equals("1"));
                Type type = new TypeToken<ArrayList<Item>>(){}.getType();
                try {
                    items = gson.fromJson(itemList, type);
                }
                catch(Exception ex) {
                    Toast.makeText(null, "Exception " + ex.getMessage(), Toast.LENGTH_LONG).show();
                }
                curse.close();
                Member res;
                Log.v("WMS-DEBUG", "DATABASE: getEntry: " + userid + " is " + (isAdmin ? "" : "not ") + "an Admin.");
                if (isAdmin)
                    res = new Admin(userid, userpass, contact, locks);
                else
                    res = new User(userid, userpass, contact, locks);
                res.setItems(items);
                return res;
            }
        }
        return null;
    }

    /**
     * Gets Member data for all Members in the database.
     * @return a list containing all Members
     */
    public Collection<Member> getAllUsers()
    {
        this.open();
        ArrayList<Member> memberList = new ArrayList<Member>();
        Cursor curse;
        String query = "SELECT * FROM " + TABLE_USER;
        curse = data.rawQuery(query, null);
        if(curse.moveToFirst())
        {
            while(!curse.isAfterLast())
            {
                memberList.add(cursorToUser(curse));
                curse.moveToNext();
            }
        }
        curse.close();
        return memberList;
    }

    /**
     * Gets Member data for the given cursor.
     * @param curse cursor of Member to get data for
     * @return Member object
     */
    private Member cursorToUser(Cursor curse)
    {
        Type type = new TypeToken<ArrayList<Item>>(){}.getType();
        gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
        ArrayList<Item> items = new ArrayList<Item>();
        String userid = curse.getString(0);
        String userpass = curse.getString(1);
        String contact = curse.getString(2);
        String itemList = curse.getString(3);
        int locks = Integer.parseInt(curse.getString(4));
        boolean isAdmin = (curse.getString(5).equals("1"));
        try {
            items = gson.fromJson(itemList, type);
        }
        catch(Exception ex) {
            Toast.makeText(null, "Exception " + ex.getMessage(), Toast.LENGTH_LONG).show();
        }
        Member res;
        if (isAdmin)
            res = new Admin(userid, userpass, contact, locks);
        else
            res = new User(userid, userpass, contact, locks);
        res.setItems(items);
        return res;
    }

    /**
     * Updates Member data for the given user name.
     * @param m user name of the Member to update
     */
    public void update(Member m)
    {
        String filter = COLUMN_USER + " = '" + m.getName() + "';";
        String items = gson.toJson(m.getItems());
        int strikes = m.getStrikes();
        ContentValues args = new ContentValues();
        args.put(COLUMN_ITEMS, items);
        args.put(COLUMN_LOCKS, strikes);
        data.update(TABLE_USER, args, filter, null);
    }

    /**
     * Deletes the database.
     */
    public void del()
    {
        help.deleteDatabase();
    }

    /**
     * Closes the database.
     */
    public void close()
    {
        data.close();
    }

    private class DBException extends Exception{
        public DBException()
        {
            super("Database has no such value-> userid or pass blank (hax0r?)");
        }
    }
}
